/*
 * Package il.ac.biu.cs.grossmm.test.cppToFooAdapter
 * File FooCppAdapter.java
 * 
 * This is a part of presence service framework API. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package il.ac.biu.cs.grossmm.tests.fooAdapters;

import static il.ac.biu.cs.grossmm.api.keys.KeyFactory.*;
import static il.ac.biu.cs.grossmm.api.keys.PatternFactory.*;
import static il.ac.biu.cs.grossmm.api.presence.BaseVocabulary.*;
import il.ac.biu.cs.grossmm.api.OperationFailedException;
import il.ac.biu.cs.grossmm.api.cpp.BaseCppConsumer;
import il.ac.biu.cs.grossmm.api.cpp.State;
import il.ac.biu.cs.grossmm.api.keys.Key;
import il.ac.biu.cs.grossmm.api.presence.Entity;
import il.ac.biu.cs.grossmm.api.presence.Policy;
import il.ac.biu.cs.grossmm.api.presence.Registrar;
import il.ac.biu.cs.grossmm.api.presence.ResourceType;
import il.ac.biu.cs.grossmm.api.server.ComponentManager;
import il.ac.biu.cs.grossmm.foo.protocol.*;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class FooCppBridge extends BaseCppConsumer<String> implements
	RequestHandler {
	FooProvider provider;

	Registrar registrar;

	Map<String, UserSession> SessionBySid =
		new ConcurrentHashMap<String, UserSession>();

	Map<UserSession, Set<String>> sidsBySession =
		new ConcurrentHashMap<UserSession, Set<String>>();

	public FooCppBridge()
	{
		super("Presence-Cpp Bridge", String.class,

		pattern(Entity.class, mandatory(CONTENT_TYPE, String.class), mandatory(
			RESOURCE_TYPE, ResourceType.class), optional(FILTER, pattern(
			String.class, mandatory(CONTENT_TYPE, String.class)))),

		null, false);
	}

	@Override
	public void setComponentManager(ComponentManager container)
		throws Exception
	{
		super.setComponentManager(container);

		provider = container.getByClass(FooProvider.class);

		provider.registerRequestHandler(SubscribeRequest.class, this);

		registrar = container.getByClass(Registrar.class);
	}

	public boolean requestReceived(Session session, Request request)
		throws OperationFailedException
	{
		assert request instanceof SubscribeRequest;

		SubscribeRequest sub = (SubscribeRequest) request;
		
		
		String resourceTypeStr = sub.getResourceType();
		
		ResourceType resourceType;
		
		if( resourceTypeStr.equals("presence") )
			resourceType = ResourceType.PRESENCE; 
		else if( resourceTypeStr.endsWith("-rules") )
			resourceType = Policy.policy(resourceTypeStr);
		else {
			session.send(new Response(request, ResponseCodes.NOT_FOUND,
				"Unsupported resource: "+resourceTypeStr));
			return true;
		}
			

		String user = sub.getWatcher();
		UserSession u = session.getUserSession(user);
		
		if (u == null) {
			session.send(new Response(request, ResponseCodes.UNAUTHORIZED));
			return true;
		}

		Entity watcher = u.getEntity();

		String resource = sub.getResource();
		
		String format = sub.getFormat();

		Entity entity = registrar.getEntity(resource);
		if (entity == null) {
			session.send(new Response(request, ResponseCodes.NOT_FOUND,
				resource));
			return true;
		}
		String filterType = sub.getFilterType();

		Key key;

		if (filterType == null) {
			key =
				key(entity, entry(CONTENT_TYPE, format), entry(
					RESOURCE_TYPE, resourceType));
		} else {
			key =
				key(entity, entry(CONTENT_TYPE, format), entry(
					RESOURCE_TYPE, resourceType), entry(FILTER, key(
					sub.getFilter(), entry(CONTENT_TYPE, sub.getFilterType()))));
		}

		String sid = sub.getSid();
		subscribed(u, sid);
		
		State state = subscribe(watcher, key, sid, sub.getDuration());

		if (state.isSuccess()) {
			session.send(new Response(request, ResponseCodes.OK));
		} else {
			unsubscribed(u, sid);
			session.send(new Response(request, ResponseCodes.FORBIDDEN,
				resource));
		}

		return true;
	}

	private synchronized void subscribed(UserSession u, String sid)
	{
		Set<String> sids = sidsBySession.get(u);

		if (sids == null) {
			sids = new HashSet<String>();
			sidsBySession.put(u, sids);
		}

		sids.add(sid);

		SessionBySid.put(sid, u);
	}
	
	private synchronized void unsubscribed(UserSession u, String sid)
	{
		SessionBySid.remove(sid);
		Set<String> sids = sidsBySession.get(u);
		if( sids == null )
			return;
		
		if( sids.size() == 1 ) 
			sidsBySession.remove(u);
		else
			sids.remove(sid);
	}
	
	public void terminating(Session session)
	{
		// TODO Implement terminating
		// 
	}

	public void notify(Entity watcher, Key target, State state,
		String subscriptID, String contents)
	{
		UserSession u = SessionBySid.get(subscriptID);
		
		if( u == null )
			return; // already unsubscribed
		
		if(! state.isSuccess() ) 
			unsubscribed(u, subscriptID);
			

		String user = u.getUser();
		Object contentType = target.value(CONTENT_TYPE);
		
		NotifyRequest notifyRequest =
			new NotifyRequest(subscriptID, user,
				state.isSuccess(), state.getDuration(),
				(String) contentType, contents);

		u.getSession().send(notifyRequest);
	}

	

	public void attached(UserSession session)
	{
		
	}

	public void dettached(UserSession session)
	{
		// TODO
	}
}
